﻿using System;
using Microsoft.Win32;

namespace SeleniumToolkit.Util
{
    /// <summary>
    /// Utility class for reading / writing to the registry.  Currently limited to HKLM
    /// </summary>
    [System.Security.Permissions.RegistryPermission(System.Security.Permissions.SecurityAction.Demand)]
    public class RegistryKeyHelper
    {
        #region Constructors
        /// <summary>
        /// Create a RegistryKeyHelper to inspect, read and write to an existing RegistryKey
        /// </summary>
        /// <param name="keyName">Registry Key Name</param>
        public RegistryKeyHelper(string keyName)
            : this(keyName, false)
        {
        }

        /// <summary>
        /// Create a RegistryKeyHelper with option to create the key if it is not present
        /// </summary>
        /// <param name="keyName">Registry Key Name</param>
        /// <param name="createIfMissing">flag to create the key if it is not found</param>
        public RegistryKeyHelper(string keyName, bool createIfMissing)
        {
            _keyName = AdjustKeyPathFor64BitProcess(keyName);
            _createIfMissing = createIfMissing;
        } 
        #endregion

        #region Properties
        /// <summary>
        /// Determines if the RegistryKey exists.  When using createIfMissing, this is always true
        /// </summary>
        public virtual bool RegistryKeyExists
        {
            get
            {
                return RegistryKey != null;
            }
        }

        /// <summary>
        /// Gets the Registry Key Name
        /// </summary>
        public string KeyName
        {
            get { return _keyName; }
        } 
        #endregion

        #region Public Methods
        /// <summary>
        /// Gets the value from the registry key
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public virtual string GetValue(string name)
        {
            if (!RegistryKeyExists) return null;

            return RegistryKey.GetValue(name) as String;
        }

        /// <summary>
        /// Sets the value in the registry
        /// </summary>
        /// <param name="name">Value Name</param>
        /// <param name="value">Value to set</param>
        /// <remarks>will throw an exception if missing and createIfMissing is false</remarks>
        public virtual void SetValue(string name, string value)
        {
            if (!RegistryKeyExists)
                throw new Exception(String.Format("Unable to write registry value \"{0}", name));

            RegistryKey.SetValue(name, value, RegistryValueKind.String);
        }

        /// <summary>
        /// Delete Registry Key
        /// </summary>
        public virtual void Delete()
        {
            Registry.LocalMachine.DeleteSubKey(_keyName);
        } 
        #endregion

        #region Private members

        /// <summary>
        /// Change incoming requests for Software to Software\Wow6432Node when running as a 64bit process
        /// </summary>
        /// <param name="keyName"></param>
        /// <returns></returns>
        /// <remarks>This allows the toolkit to remain ANY CPU, but provide Registry Redirection for 32-bit compatibility</remarks>
        private static string AdjustKeyPathFor64BitProcess(string keyName)
        {
            int indexOfSoftware = keyName.IndexOf("software", StringComparison.OrdinalIgnoreCase);

            if (IntPtr.Size == 8 &&   // process is 64bit when ptr size is 8 bits
                indexOfSoftware == 0) // our string starts with "software"
            {
                // insert our 32 compatibility node into the key
                return keyName.Insert(8, @"\Wow6432Node");
            }
            return keyName;
        }

        private RegistryKey RegistryKey
        {
            get
            {
                if (_key == null)
                {
                    _key = Registry.LocalMachine.OpenSubKey(_keyName, true);
                    if (_key == null && _createIfMissing)
                    {
                        Registry.LocalMachine.CreateSubKey(_keyName);
                        _key = Registry.LocalMachine.OpenSubKey(_keyName, true);
                    }
                }
                return _key;
            }
        } 
        #endregion

        private readonly string _keyName;
        private readonly bool _createIfMissing;
        private RegistryKey _key;
    }
}
